/**
 *  SOsim: Simulator for Operating Systems Education
 *
 * 		SOsim is a simulator with visual facilities to serve as an effective 
 *  support tool for the better teaching and learning of the concepts and techniques 
 *  in modern operating systems, serving as a way to render the whole process more 
 *  efficient.
 *  Copyright (C) 2011  Luiz Paulo Maia
 *
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
package br.org.sosim.controller;

import java.util.ArrayList;

/**
 * Class TKernel
 */
public class TKernel {
	protected ArrayList<ArrayList<TTipoInt>> nivelInterrupt;
	protected int vetLevel = 0; //TODO saber o nome melhor para essa variavel esta muito vago...VET e o que?
	public int quantum = 0;
	// public Form Form = null; TODO
	
	public ArrayList<TProcesso> processList = null;
	// Lista de processos
	public ArrayList<ArrayList<TProcesso>> processes;
	
	/**
	 * This is the constructor of class TKernel
	 * @param createSuspended
	 */
	public TKernel(boolean createSuspended) {
		quantum = 1000;
		
		processList = new ArrayList<TProcesso>();
		processes = new ArrayList<ArrayList<TProcesso>>();
		
		for (int i = u_Consts.Units.u_Consts.MIN_PRIO; i <= u_Consts.Units.u_Consts.MAX_PRIO; i++)
			processes.add(new ArrayList<TProcesso>());
		
		for (int i = Units.u_Kernel.MIN_INTLEV; i <= Units.u_Kernel.MAX_INTLEV; i++)
			nivelInterrupt.add(new ArrayList<TTipoInt>());
	}
	
	/**
	 * This method accept interruption
	 * @param typeInt
	 */
	public void acceptInterruption(TTipoInt typeInt) {
		int tempInt = u_Kernel.TabVetInt[typeInt.ordinal()].priority;
		nivelInterrupt.get(tempInt).add(typeInt);
	}
	
	/**
	 * This method add a process in scheduler
	 * @param process
	 */
	public void addProcessScheduler(TProcesso process) {
		if (process != null) {
			processes.get(process.prioridade).add(process);
			process.tempoEntradaFilaPronto = f_Console.Units.f_Console.Clock.TempoTotalSim;
			
			switch (process.estado) {
				case u_Processo.TEstadoProcesso.SuspOut:
				case u_Processo.TEstadoProcesso.IOOut:
				case u_Processo.TEstadoProcesso.PFOut:
					process.Estado = u_Processo.TEstadoProcesso.ProntoOut;
					break;
				default:
					process.Estado = u_Processo.TEstadoProcesso.Pronto;
					break;
			}
			process.Shape.Parent = form;
		}
	}
	
	/**
	 * This method alter the priority base
	 * @param process
	 * @param priority
	 */
	public void alterPriorityBase(TProcesso process, int priority) {
		process.prioBase = priority;
		if ((process.Estado == u_Processo.TEstadoProcesso.Pronto)
				|| (process.Estado == u_Processo.TEstadoProcesso.ProntoOut)) {
			alterPriorityBase_RemoveProcessScheduler(process);
			processes.get(priority).add(process);
		}
	}
	
	/**
	 * This method alters the priority base and remove a process of scheduler
	 * @param process
	 */
	public void alterPriorityBase_RemoveProcessScheduler(TProcesso process) {
		fim: for (int i = u_Consts.Units.u_Consts.MIN_PRIO; i <= u_Consts.Units.u_Consts.MAX_PRIO; i++)
			for (int j = (processes.get(i).Count - 1); j >= 0; j--)
				if ((((TProcesso) (processes.get(i).get(j))).PID == process.PID)) {
					processes.get(i).remove(j);
					break fim;
				}
	}
	
	/**
	 * This method create a process
	 * @param process
	 */
	public void createProcess(TProcesso process) {
		processList.add(process);
		addProcessScheduler(process);
	}
	
	/**
	 * This method kills a process
	 * @param process
	 */
	public void killProcess(TProcesso process) {
		processList.remove(processList.indexOf(process));
		process.elimina(process);
		process = null;
	}
	
	/**
	 * This method returns the number of interrupts
	 * @return
	 */
	public int numberInterrupts() {
		return nivelInterrupt.get(0).size();
	}
	
	/**
	 * This method counts the number of process by state
	 * @param nExec
	 * @param nPronto
	 * @param nEspera
	 */
	public void countsProcessNumberByState(Integer nExec, Integer nPronto, Integer nEspera) {
		nExec = 0;
		nPronto = 0;
		nEspera = 0;
		
		TProcesso tempProcess = null; 
		
		for (int i = 0; i <= (processList.size() - 1); i++) {
			tempProcess = processList.get(i);
			
			switch (tempProcess.Estado) {
				case u_Processo.TEstadoProcesso.Pronto:
					nPronto = nPronto + 1;
					break;
				case u_Processo.TEstadoProcesso.Exec:
					nExec = nExec + 1;
					break;
				case u_Processo.TEstadoProcesso.IO:
				case u_Processo.TEstadoProcesso.Susp:
				case u_Processo.TEstadoProcesso.PF:
					nEspera = nEspera + 1;
					break;
			}
		}
	}
	
	/**
	 * This method returns the last process in the queue
	 * @return
	 */
	public int returnsTop() {
		int result = u_Consts.Units.u_Consts.MIN_PRIO - 1;
		
		for (int i = u_Consts.Units.u_Consts.MAX_PRIO; i >= u_Consts.Units.u_Consts.MIN_PRIO; i--)
			if (processes.get(i).size() > 0) {
				result = i;
				break;
			}
		
		return result;
	}
	
	/**
	 * This method selects a process available
	 * @param processAvailable
	 */
	public void selectsProcessAvailable(TProcesso processAvailable) {
		TProcesso processSelected;
		
		processAvailable = null;
		
		for (int i = u_Consts.Units.u_Consts.MAX_PRIO; i >= u_Consts.Units.u_Consts.MIN_PRIO; i--)
			if ((processes.get(i).size() > 0)) {
				processSelected = processes.get(i).get(0);;
				if ((processSelected.Estado == u_Processo.TEstadoProcesso.ProntoOut)){
					f_Console.Units.f_Console.MemoReal.SwapIn(processSelected);
				}
					
				f_Console.Units.f_Console.NumProcessosEscalonados = f_Console.Units.f_Console.NumProcessosEscalonados + 1;
				
				// Para calculo do throughput
				f_Console.Units.f_Console.TempoAcumuladoProcessosPronto = f_Console.Units.f_Console.TempoAcumuladoProcessosPronto
						+ (f_Console.Units.f_Console.Clock.TempoTotalSim - processSelected.TempoEntradaFilaPronto);
				
				processAvailable = processSelected;
				processes.get(i).remove(0);
				break;
			}
	}
	
	/**
	 * This method selects a process in ready state
	 * @param process
	 */
	public void selectsProcessReady(TProcesso process) {
		fim: for (int i = u_Consts.Units.u_Consts.MIN_PRIO; i <= u_Consts.Units.u_Consts.MAX_PRIO; i++)
			for (int j = (processes.get(i).size() - 1); j >= 0; j--)
				if (processes.get(i).get(j).Estado != u_Processo.TEstadoProcesso.ProntoOut) {
					process = processes.get(i).get(j);
					processes.get(i).get(j).Estado = u_Processo.TEstadoProcesso.ProntoOut;
					break fim;
				}
	}
	
	/**
	 * This method suspend a process
	 * @param process
	 */
	public void suspendProcess(TProcesso process) {
		f_Log.Units.f_Log.frmLog.AddLine(u_Consts.Units.u_Consts.msglog0600
				+ (process.PID).ToString()
				+ u_Consts.Units.u_Consts.msglog0800, process.Cor);
		f_Console.Units.f_Console.Espera.AddProcesso(process,
				u_Processo.TEstadoProcesso.Susp);
		process = null;
	}
	
	/**
	 * This method verify ready state of process
	 * @param total
	 * @param numberProcess
	 */
	public void verifyReadyState(Integer total, Integer numberProcess) {
		total = 0;
		numberProcess = 0;
		
		TProcesso tempProcess = null;
		
		for (int i = 0; i <= (processList.size() - 1); i++) {
			tempProcess = processList.get(i);
			
			if ((tempProcess.Estado == u_Processo.TEstadoProcesso.Pronto)) {
				total = total
						+ (f_Console.Units.f_Console.Clock.TempoTotalSim - tempProcess.TempoEntradaFilaPronto);
				numberProcess++;
			}
		}
	}
	
	/**
	 * This method verify interrupt
	 * @return
	 */
	public TTipoInt verifyInterrupt() {
		TTipoInt result = TTipoInt.ti_None;
		
		for (int i = Units.u_Kernel.MIN_INTLEV; i <= Units.u_Kernel.MAX_INTLEV; i++)
			if (nivelInterrupt.get(i).size() > 0) {
				result = nivelInterrupt.get(i).get(0);
				nivelInterrupt.get(i).remove(0);
				break;
			}
		
		return result;
	}
}
